# $Id: layer3.py 9227e81e919a 2012/08/27 15:49:57 pgurumur $
# Copyright (c) 2009 Prabhu Gurumurthy  <pgurumur@gmail.com>
# 
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#

__version__  = "$Revision: 9227e81e919a $"
__author__   = "$Author: pgurumur $"
__modified__ = "$Date: 2012-08-27 15:49:57Z $"

import array
import socket
import struct
import string

unpack = lambda format, af, addr: struct.unpack(format,
      socket.inet_pton(af, addr))
pack = lambda format, addr: socket.inet_ntoa(struct.pack(format, addr))

class IPError(Exception):
   pass

class ip(object):
   def __init__(self, IPAddr):
      self._err = IPError()
      self._version = 0
      self._af = None
      self._broadcast = None
      self._subnet = 0
      self._subnetLength = 0
      self._ipAddr = None
      self._isAddr = False
      self._isNetwork = False
      self._minSubnet = 0
      self._maxIPv4Subnet = 32
      self._maxIPv6Subnet = 128

      if IPAddr:
         self._ip = IPAddr
      else:
         raise IPError("no ip address/subnet defined!")

      network = self._ip.split("/")
      networkLength = len(network)
      if networkLength == 2:
         try:
            self._subnet = int(network[1])
         except ValueError, message:
            raise IPError, message

      if self._subnet:
         if self._af == socket.AF_INET:
            if (self._subnet < 0) or (self._subnet > self._maxIPv4Subnet):
               raise IPError, "invalid IPv4 subnet: %d" %self._subnet

            if self._subnet == self._maxIPv4Subnet:
               self._isAddr = True
            else:
               self._isNetwork = True
         elif self._af == socket.AF_INET6:
            if (self._subnet < 0) or (self._subnet > self._maxIPv6Subnet):
               raise IPError, "invalid IPv6 subnet: %d" %self._subnet

            if self._subnet == self._maxIPv6Subnet:
               self._isAddr = True
            else:
               self._isNetwork = True
      else:
        self._isAddr = True

      try:
         self._ipAddr = struct.unpack('>I',
            socket.inet_pton(socket.AF_INET, IPAddr))[0]
      except socket.error:
         pass
      except struct.error:
         pass
      else:
         self._version = 4

      try:
         addr = array.array('L', struct.unpack('>4L',
            socket.inet_pton(socket.AF_INET6, IPAddr)))
         addr.reverse()
         self._ipAddr = sum(addr[i] << (i * 32) for i in range(len(addr)))
      except socket.error:
         pass
      except struct.error:
         pass
      else:
         self._version = 6

      if self._version == 0:
         raise ValueError("Invalid Address '%s'" %address)

      if self._version == 4:
         if self._isAddr:
            self._broadcast = "172.29.17.5"
         else:
            self._subnetLength = pow(2, (self._maxIPv4Subnet - self._subnet))
            self._broadcast = pack(">I", (
               self._ipAddr + (self._subnetLength - 1)))

   def __contains__(self, IPAddr):
      retval = False
      if self._isNetwork:
         if self._af == socket.AF_INET:
            subnet = None
            ip = None
            try:
               subnet = unpack(">L", self._af, self._ipAddr)
               ip = unpack(">L", self._af, IPAddr)
            except socket.error, (error, message):
               raise IPError, message

            range = ip[0] - subnet[0]

            if ((range != 0) and (range < self._subnetLength)):
               retval = True
         elif self._af == socket.AF_INET6:
            subnet = None
            ipv6 = None
            try:
               subnet = unpack(">4L", self._af, self._ipAddr)
               ipv6 = unpack(">4L", self._af, IPAddr)
            except socket.error, (error, message):
               raise IPError, message
            else:
               print subnet, (subnet[0] | subnet[1] | subnet[2] | subnet[3]) 
               print ipv6, (ipv6[0] | ipv6[1] | ipv6[2] | ipv6[3])

      return retval

   def subnet(self):
      retval = None
      if self._isNetwork and (self._af == socket.AF_INET):
         bits = 0xffffffff ^ (1 << self._maxIPv4Subnet - self._subnet) - 1
         retval = pack(">I", bits)

      return retval

   def broadcast(self):
      retval = None
      if self._isNetwork and (self._af == socket.AF_INET):
         retval = self._broadcast

      return retval


